# MATT (Microsoft 365 Conditional Access Triage Tool) # Secure Guard Consulting # Files will be saved to C:\Temp folder # V2 (version 2) - prior script wasn't including specific Grants chosen. This script will show Grants. # 1. Install modules if missing if (-not (Get-Module -ListAvailable -Name Microsoft.Graph)) { Install-Module Microsoft.Graph -Scope CurrentUser -Force } if (-not (Get-Module -ListAvailable -Name ImportExcel)) { Install-Module ImportExcel -Scope CurrentUser -Force } Import-Module Microsoft.Graph Import-Module ImportExcel # 2. Connect to Microsoft Graph Connect-MgGraph -Scopes "Policy.Read.All", "Directory.Read.All", "Application.Read.All", "Group.Read.All", "User.Read.All" # 3. Preload directory objects Write-Host "📥 Loading directory references..." $userMap = @{}; Get-MgUser -All | ForEach-Object { $userMap[$_.Id] = $_.DisplayName } $groupMap = @{}; $groupMembers = @{} Get-MgGroup -All | ForEach-Object { $groupMap[$_.Id] = $_.DisplayName $groupMembers[$_.Id] = (Get-MgGroupMember -GroupId $_.Id -All | ForEach-Object { $_.DisplayName }) -join ", " } $roleMap = @{} Get-MgDirectoryRole | ForEach-Object { $roleMap[$_.Id] = $_.DisplayName } Get-MgDirectoryRoleTemplate -All | ForEach-Object { if (-not $roleMap.ContainsKey($_.Id)) { $roleMap[$_.Id] = $_.DisplayName } } $appMap = @{}; Get-MgServicePrincipal -All | ForEach-Object { $appMap[$_.Id] = $_.DisplayName } $locationMap = @{} Get-MgIdentityConditionalAccessNamedLocation -All | ForEach-Object { $desc = $_.DisplayName if ($_.AdditionalProperties.'@odata.type' -like '*ipNamedLocation') { $desc += " (" + ($_.IpRanges | ForEach-Object { $_.CidrAddress }) -join ", " + ")" } elseif ($_.AdditionalProperties.'@odata.type' -like '*countryNamedLocation') { $desc += " (Countries: " + ($_.CountriesAndRegions -join ", ") + ")" } $locationMap[$_.Id] = $desc } function Resolve { param($ids, $map, $type = "id") if (-not $ids) { return "" } return ($ids | ForEach-Object { if ($map[$_]) { if ($type -eq "group" -and $groupMembers[$_]) { "$($map[$_]) (Members: $($groupMembers[$_]))" } else { $map[$_] } } else { $_ } }) -join ", " } # 4. Load CA policies $policies = Get-MgIdentityConditionalAccessPolicy $total = $policies.Count # Custom row order $preferredOrder = @( "State", "CreatedDateTime", "ModifiedDateTime", "IncludeUsers", "ExcludeUsers", "IncludeGroups", "ExcludeGroups", "IncludeRoles", "ExcludeRoles", "TargetResources", "IncludePlatforms", "ExcludePlatforms", "ClientAppTypes", "IncludeLocations", "ExcludeLocations", "DeviceStates", "SignInRiskLevels", "UserRiskLevels", "GrantControls", "GrantControls: Operator", "GrantControls: Require MFA", "GrantControls: Require Phishing-Resistant MFA", "GrantControls: Require Compliant Device", "GrantControls: Require Domain-Joined Device", "GrantControls: Require Approved Client App", "GrantControls: Require App Protection Policy", "GrantControls: Require Authentication Strength", "GrantControls: Authentication Strength Name", "GrantControls: Terms of Use", "SessionControls: SignInFrequency", "SessionControls: CloudAppSecurity", "SessionControls: ApplicationEnforcedRestrictions", "SessionControls: PersistentBrowser", "AuthenticationFlows: TransferMethods" ) $matrix = @{} foreach ($s in $preferredOrder) { $matrix[$s] = @{} } $i = 1 foreach ($policy in $policies) { $name = $policy.DisplayName Write-Host "➡️ [$i/$total] $name"; $i++ $matrix["CreatedDateTime"][$name] = $policy.CreatedDateTime.ToString("yyyy-MM-dd HH:mm") $matrix["ModifiedDateTime"][$name] = $policy.ModifiedDateTime.ToString("yyyy-MM-dd HH:mm") $matrix["State"][$name] = $policy.State $matrix["IncludeUsers"][$name] = Resolve $policy.Conditions.Users.IncludeUsers $userMap $matrix["ExcludeUsers"][$name] = Resolve $policy.Conditions.Users.ExcludeUsers $userMap $matrix["IncludeGroups"][$name] = Resolve $policy.Conditions.Users.IncludeGroups $groupMap "group" $matrix["ExcludeGroups"][$name] = Resolve $policy.Conditions.Users.ExcludeGroups $groupMap "group" $matrix["IncludeRoles"][$name] = Resolve $policy.Conditions.Users.IncludeRoles $roleMap $matrix["ExcludeRoles"][$name] = Resolve $policy.Conditions.Users.ExcludeRoles $roleMap $matrix["TargetResources"][$name] = if ($policy.Conditions.Applications.IncludeAllApplications) { "All Apps" } else { Resolve $policy.Conditions.Applications.IncludeApplications $appMap } $matrix["IncludePlatforms"][$name] = ($policy.Conditions.Platforms.IncludePlatforms -join ", ") $matrix["ExcludePlatforms"][$name] = ($policy.Conditions.Platforms.ExcludePlatforms -join ", ") $matrix["ClientAppTypes"][$name] = ($policy.Conditions.ClientAppTypes -join ", ") $matrix["IncludeLocations"][$name] = Resolve $policy.Conditions.Locations.IncludeLocations $locationMap $matrix["ExcludeLocations"][$name] = Resolve $policy.Conditions.Locations.ExcludeLocations $locationMap $matrix["DeviceStates"][$name] = ($policy.Conditions.DeviceStates.IncludeStates -join ", ") $matrix["SignInRiskLevels"][$name] = ($policy.Conditions.SignInRiskLevels -join ", ") $matrix["UserRiskLevels"][$name] = ($policy.Conditions.UserRiskLevels -join ", ") $controls = $policy.GrantControls.BuiltInControls $matrix["GrantControls"][$name] = $controls -join ", " if ($policy.GrantControls.Operator -and $policy.GrantControls.Operator -ne "OR") { $matrix["GrantControls: Operator"][$name] = $policy.GrantControls.Operator } if ($controls -contains "mfa") { $matrix["GrantControls: Require MFA"][$name] = "TRUE" } if ($controls -contains "requirePhishingResistantAuthentication") { $matrix["GrantControls: Require Phishing-Resistant MFA"][$name] = "TRUE" } if ($controls -contains "compliantDevice") { $matrix["GrantControls: Require Compliant Device"][$name] = "TRUE" } if ($controls -contains "domainJoinedDevice") { $matrix["GrantControls: Require Domain-Joined Device"][$name] = "TRUE" } if ($controls -contains "approvedApplication") { $matrix["GrantControls: Require Approved Client App"][$name] = "TRUE" } if ($controls -contains "applicationEnforcedRestrictions") { $matrix["GrantControls: Require App Protection Policy"][$name] = "TRUE" } if ($policy.GrantControls.AuthenticationStrength) { $matrix["GrantControls: Require Authentication Strength"][$name] = "TRUE" $matrix["GrantControls: Authentication Strength Name"][$name] = $policy.GrantControls.AuthenticationStrength.DisplayName } if ($policy.GrantControls.TermsOfUse) { $matrix["GrantControls: Terms of Use"][$name] = $policy.GrantControls.TermsOfUse -join ", " } if ($policy.Conditions.PSObject.Properties.Name -contains "AuthenticationFlows") { $af = $policy.Conditions.AuthenticationFlows if ($af.transferMethods) { $matrix["AuthenticationFlows: TransferMethods"][$name] = $af.transferMethods } } $sc = $policy.SessionControls | ConvertTo-Json -Depth 5 | ConvertFrom-Json if ($sc) { if ($sc.SignInFrequency.IsEnabled) { $summary = "TRUE" if ($sc.SignInFrequency.Value) { $summary += " ($($sc.SignInFrequency.Value) $($sc.SignInFrequency.Type))" } if ($sc.SignInFrequency.AuthenticationType) { $summary += " - $($sc.SignInFrequency.AuthenticationType)" } $matrix["SessionControls: SignInFrequency"][$name] = $summary } if ($sc.CloudAppSecurity.IsEnabled) { $matrix["SessionControls: CloudAppSecurity"][$name] = $sc.CloudAppSecurity.CloudAppSecurityType } if ($sc.ApplicationEnforcedRestrictions.IsEnabled) { $matrix["SessionControls: ApplicationEnforcedRestrictions"][$name] = "TRUE" } if ($sc.PersistentBrowser.IsEnabled) { $matrix["SessionControls: PersistentBrowser"][$name] = $sc.PersistentBrowser.Mode } } } # 5. Sort rows $orderedSettings = @() foreach ($item in $preferredOrder) { $matches = $matrix.Keys | Where-Object { $_ -eq $item -or $_ -like "${item}:*" } $orderedSettings += $matches } $remaining = $matrix.Keys | Where-Object { ($_ -like "SessionControls:*") -and ($_ -notin $orderedSettings) } $orderedSettings += ($remaining | Sort-Object) # 6. Build final export table $final = @() foreach ($row in $orderedSettings) { $entry = [ordered]@{ "Setting" = $row } foreach ($col in $matrix[$row].Keys) { $entry[$col] = $matrix[$row][$col] } $final += New-Object PSObject -Property $entry } # 7. Export to Excel $path = "C:\Temp\CA_Matrix.xlsx" $final | Export-Excel -Path $path -AutoSize Write-Host "`n✅ Done! File saved to:`n$path" # 8. Disconnect and clear token cache Disconnect-MgGraph -ErrorAction SilentlyContinue Remove-Item "$env:USERPROFILE\.graph" -Recurse -Force -ErrorAction SilentlyContinue Remove-Item "$env:LOCALAPPDATA\.IdentityService\msal.cache" -Force -ErrorAction SilentlyContinue Write-Host "`n🧹 Microsoft Graph token cache cleared. Reauthentication will be required on next run."